/* LIBDGL -- a Directed Graph Library implementation
 * Copyright (C) 2002 Roberto Micarelli
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
 * Source best viewed with tabstop=4
 */

#include<stdio.h>
#include<regex.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>

#include "opt.h"
#include "../type.h"
#include "../graph.h"

static void _regmtostring( char * pszOut , int cszOut , char * pszIn , regmatch_t * pregm )
{
    int i , iout;
    for ( iout = 0 , i = pregm->rm_so ; i < pregm->rm_eo && iout < cszOut-1 ; i ++ ) {
        if ( i >= 0 ) pszOut[iout++] = pszIn[i];
    }
    pszOut[iout] = 0;
}

static int _sztoattr( unsigned char * pbNodeAttr, int cbNodeAttr, char * szw ) {
	int i , ib;
	for( ib = 0 , i = 0 ; szw[i] && ib < cbNodeAttr ; i ++ ) {
		if ( szw[i] == ' ' ) continue;
		pbNodeAttr[ib] = 	(szw[i]>='0'&&szw[i]<='9')?(szw[i]-'0')*16:
							(szw[i]>='A'&&szw[i]<='F')?(10+(szw[i]-'A'))*16:
							(szw[i]>='a'&&szw[i]<='f')?(10+(szw[i]-'a'))*16:
							0;
		i++;
		if ( szw[i] ) {
			pbNodeAttr[ib] += 	(szw[i]>='0'&&szw[i]<='9')?(szw[i]-'0'):
								(szw[i]>='A'&&szw[i]<='F')?(10+(szw[i]-'A')):
								(szw[i]>='a'&&szw[i]<='f')?(10+(szw[i]-'a')):
								0;
		}
		ib++;
	}
	return ib;
}

int main( int argc , char ** argv ) {
	FILE * fp;
	char sz[1024];
	char szw[1024];
	int nret;
	regmatch_t aregm[64];
	dglInt32_t nVersion;
	dglInt32_t nNodeAttrSize;
	dglInt32_t nEdgeAttrSize;
	dglInt32_t anOpaque[16];
	int i , fd , cOut;

	regex_t reVersion;
	regex_t reByteOrder;
	regex_t reNodeAttrSize;
	regex_t reEdgeAttrSize;
	regex_t reCounters;
	regex_t reOpaque;
	regex_t reNodeFrom;
	regex_t reNodeAttr;
	regex_t reEdge;
	regex_t reToNodeAttr;
	regex_t reEdgeAttr;


	dglInt32_t	nNodeFrom , nNodeTo , nUser , nCost;

	int fInOpaque;
	int fInBody;

	unsigned char * pbNodeAttr , * pbEdgeAttr , * pbToNodeAttr;

	struct stat statdata;

	dglGraph_s graphOut;

	/* program options
	 */
 	char	*	pszFilein;
 	char	*	pszGraphout;
 
	GNO_BEGIN/* short  long        default     variable        help */
 	GNO_OPTION( "i", 	"input", 	NULL ,  	& pszFilein ,	"Input text file" )
 	GNO_OPTION( "o", 	"output", 	NULL ,  	& pszGraphout ,	"Output graph file" )
 	GNO_END
 

	if ( GNO_PARSE( argc , argv ) < 0 )
	{
		return 1;
	}
	/*
	 * options parsed
	 */

	if ( pszFilein == NULL )
	{
		GNO_HELP("... usage");
		return 1;
	}

	/*
	 * compile header expressions
	 */
	printf( "Compile header expressions..."); fflush(stdout);
	i = 0;
	if ( regcomp( &reVersion,"^Version:[ ]+([0-9]+)" , REG_EXTENDED ) != 0) goto regc_error;
	i++;
	if ( regcomp( &reByteOrder,"^Byte Order:[ ]+(.+)" , REG_EXTENDED ) != 0) goto regc_error;
	i++;
	if ( regcomp( &reNodeAttrSize,"^Node Attribute Size:[ ]+([0-9]+)" , REG_EXTENDED ) != 0) goto regc_error;
	i++;
	if ( regcomp( &reEdgeAttrSize,"^Edge Attribute Size:[ ]+([0-9]+)" , REG_EXTENDED ) != 0) goto regc_error;
	i++;
	if ( regcomp( &reCounters,"^Counters:[ ]+.*" , REG_EXTENDED ) != 0) goto regc_error;
	i++;
	if ( regcomp( &reOpaque,"^Opaque Settings:" , REG_EXTENDED ) != 0) goto regc_error;
	i++;
	printf( "done.\n" );

	/*
	 * compile body expressions
	 */

	printf( "Compile body expressions..."); fflush(stdout);
	if ( regcomp( &reNodeFrom, "^HEAD ([0-9]+)[ ]*- [HT/']+" , REG_EXTENDED ) != 0) goto regc_error;
	i++;
	if ( regcomp( &reNodeAttr, ".*HEAD ATTR [[]([0-9a-fA-F ]+)]" , REG_EXTENDED ) != 0) goto regc_error;
	i++;

	if (regcomp(&reEdge, "^EDGE #([0-9]+)[ ]*: TAIL ([0-9]+)[ ]*- [HT/']+[ ]+- COST ([0-9]+)[ ]*- ID ([0-9]+)", REG_EXTENDED) != 0) goto regc_error;
	i++;
	if (regcomp(&reToNodeAttr, ".*TAIL ATTR [[]([0-9a-fA-F ]+)]", REG_EXTENDED) != 0) goto regc_error;
	i++;
	if (regcomp(&reEdgeAttr,   ".*EDGE ATTR [[]([0-9a-fA-F ]+)]", REG_EXTENDED) != 0) goto regc_error;
	i++;

	printf( "done.\n" );

	goto regc_ok;




regc_error:
	fprintf( stderr, "regex compilation error %d\n", i );
	exit(1);





regc_ok:

	if ( (fp = fopen( pszFilein , "r" )) == NULL )
	{
		perror( "fopen" ); return 1;
	}

	fstat( fileno(fp), &statdata );

	fInOpaque = 0;
	fInBody = 0;

	nNodeAttrSize = 0;
	nEdgeAttrSize = 0;
	pbNodeAttr = NULL;
	pbToNodeAttr = NULL;
	pbEdgeAttr = NULL;

	cOut = 0;

	while( fgets( sz , sizeof( sz ) , fp ) )
	{
#ifndef VERBOSE
		if ( !(cOut++ % 512) || ftell(fp) == statdata.st_size )
			printf( "Parse input file ... status: %ld/%ld\r", ftell(fp), statdata.st_size ); fflush(stdout);
#endif

#ifdef VERYVERBOSE
	printf( "<<<%s>>>\n", sz );
#endif
		if ( fInOpaque == 0 && fInBody == 0 ) {
			if ( regexec( &reVersion, sz, 64, aregm, 0 ) == 0 ) {
				_regmtostring( szw , sizeof(szw) , sz , & aregm[1] );
				nVersion = atoi( szw );
#ifdef VERYVERBOSE
	printf( "-- version %d\n", nVersion );
#endif
			}
			else if ( regexec( &reByteOrder, sz, 64, aregm, 0 ) == 0 ) {
			}
			else if ( regexec( &reNodeAttrSize, sz, 64, aregm, 0 ) == 0 ) {
				_regmtostring( szw , sizeof(szw) , sz , & aregm[1] );
				nNodeAttrSize = atoi( szw );
				if ( nNodeAttrSize ) {
					pbNodeAttr = (unsigned char *) malloc( nNodeAttrSize );
					if ( pbNodeAttr == NULL ) {
						fprintf( stderr , "Memory Exhausted\n" ); exit(1);
					}
					pbToNodeAttr = (unsigned char *) malloc( nNodeAttrSize );
					if ( pbToNodeAttr == NULL ) {
						fprintf( stderr , "Memory Exhausted\n" ); exit(1);
					}
				}
#ifdef VERYVERBOSE
				printf( "-- node attr size %d\n", nNodeAttrSize );
#endif
			}
			else if ( regexec( &reEdgeAttrSize, sz, 64, aregm, 0 ) == 0 ) {
				_regmtostring( szw , sizeof(szw) , sz , & aregm[1] );
				nEdgeAttrSize = atoi( szw );
				if ( nEdgeAttrSize > 0 ) {
					pbEdgeAttr = (unsigned char *) malloc( nEdgeAttrSize );
					if ( pbEdgeAttr == NULL ) {
						fprintf( stderr , "Memory Exhausted\n" ); exit(1);
					}
				}
#ifdef VERYVERBOSE
				printf( "-- edge attr size %d\n", nEdgeAttrSize );
#endif
			}
			else if ( regexec( &reOpaque, sz, 64, aregm, 0 ) == 0 ) {
#ifdef VERYVERBOSE
	printf( "-- opaque...\n" );
#endif
				fInOpaque = 1;
			}
			else if ( strncmp( sz , "--" , 2) == 0 ) {
				nret = dglInitialize(
							& graphOut,
							nVersion,
							nNodeAttrSize,
							nEdgeAttrSize,
							anOpaque
							);
				if ( nret < 0 ) {
					fprintf( stderr, "dglInitialize error %s\n", dglStrerror( & graphOut ) );
					exit(1);
				}
#ifdef VERBOSE
				printf( "Initialize: Version=%ld NodeAttr=%ld EdgeAttr=%ld\n",
							nVersion, nNodeAttrSize, nEdgeAttrSize );
#endif
				fInBody = 1;
			}
		}
		else if ( fInOpaque > 0 && fInBody == 0 ) {
			if ( fInOpaque == 1 ) {
				sscanf( sz , "%ld %ld %ld %ld",
					& anOpaque[0],
					& anOpaque[1],
					& anOpaque[2],
					& anOpaque[3] );
				fInOpaque ++;
#ifdef VERYVERBOSE
	printf("opaque 1: %ld %ld %ld %ld\n",
					anOpaque[0],
					anOpaque[1],
					anOpaque[2],
					anOpaque[3] );
#endif

			}
			else if ( fInOpaque == 2 ) {
				sscanf( sz , "%ld %ld %ld %ld",
					& anOpaque[4],
					& anOpaque[5],
					& anOpaque[6],
					& anOpaque[7] );
#ifdef VERYVERBOSE
	printf("opaque 2: %ld %ld %ld %ld\n",
					anOpaque[4],
					anOpaque[5],
					anOpaque[6],
					anOpaque[7] );
#endif
				fInOpaque ++;
			}
			else if ( fInOpaque == 3 ) {
				sscanf( sz , "%ld %ld %ld %ld",
					& anOpaque[8],
					& anOpaque[9],
					& anOpaque[10],
					& anOpaque[11] );
#ifdef VERYVERBOSE
	printf("opaque 3: %ld %ld %ld %ld\n",
					anOpaque[8],
					anOpaque[9],
					anOpaque[10],
					anOpaque[11] );
#endif
				fInOpaque ++;
			}
			else if ( fInOpaque == 4 ) {
				sscanf( sz , "%ld %ld %ld %ld",
					& anOpaque[12],
					& anOpaque[13],
					& anOpaque[14],
					& anOpaque[15] );
#ifdef VERYVERBOSE
	printf("opaque 4: %ld %ld %ld %ld\n",
					anOpaque[12],
					anOpaque[13],
					anOpaque[14],
					anOpaque[15] );
#endif
				fInOpaque = 0;
			}
		}
		else if ( fInBody == 1 ) {
			if ( regexec( &reNodeFrom, sz, 64, aregm, 0 ) == 0 ) {
				_regmtostring( szw , sizeof(szw) , sz , & aregm[1] );
#ifdef VERYVERBOSE
				printf( "node from snippet = %s\n", szw);
#endif
				nNodeFrom = atol(szw);
				if ( nNodeAttrSize > 0 ) {
					if ( regexec( &reNodeAttr, sz, 64, aregm, 0 ) == 0 ) {
						_regmtostring( szw , sizeof(szw) , sz , & aregm[1] );
						if ( _sztoattr( pbNodeAttr, nNodeAttrSize, szw ) != nNodeAttrSize ) {
							fprintf( stderr, "node attr size mismatch\n" );
						}
#ifdef VERYVERBOSE
						{
								int k;
								for(k=0;k<nNodeAttrSize;k++) {
								printf("%02x", pbNodeAttr[k]);
								}
								printf("\n");
						}
#endif
					}
				}
			}
			else if ( regexec( &reEdge, sz, 64, aregm, 0 ) == 0 ) {
				_regmtostring( szw , sizeof(szw) , sz , & aregm[2] );
				nNodeTo = atol(szw);
				_regmtostring( szw , sizeof(szw) , sz , & aregm[3] );
				nCost = atol(szw);
				_regmtostring( szw , sizeof(szw) , sz , & aregm[4] );
				nUser = atol(szw);
				if ( nEdgeAttrSize > 0 ) {
					if ( regexec( &reEdgeAttr, sz, 64, aregm, 0 ) == 0 ) {
						_regmtostring( szw , sizeof(szw) , sz , & aregm[1] );
						if ( _sztoattr( pbEdgeAttr, nEdgeAttrSize, szw ) != nEdgeAttrSize ) {
							fprintf( stderr, "edge attr size mismatch\n" );
						}
#ifdef VERYVERBOSE
						{
								int k;
								for(k=0;k<nEdgeAttrSize;k++) {
								printf("%02x", pbEdgeAttr[k]);
								}
								printf("\n");
						}
#endif
					}
				}
				if ( nNodeAttrSize > 0 ) {
					if ( regexec( &reToNodeAttr, sz, 64, aregm, 0 ) == 0 ) {
						_regmtostring( szw , sizeof(szw) , sz , & aregm[1] );
						if ( _sztoattr( pbToNodeAttr, nNodeAttrSize, szw ) != nNodeAttrSize ) {
							fprintf( stderr, "to node attr size mismatch\n" );
						}
#ifdef VERYVERBOSE
						{
								int k;
								for(k=0;k<nNodeAttrSize;k++) {
								printf("%02x", pbToNodeAttr[k]);
								}
								printf("\n");
						}
#endif
					}
				}
				nret = dglAddEdgeX(
								& graphOut,
								nNodeFrom,
								nNodeTo,
								nCost,
								nUser,
								pbNodeAttr,
								pbToNodeAttr,
								pbEdgeAttr,
								0 );

				if ( nret < 0 ) {
					fprintf( stderr, "dglAddEdge error %s\n", dglStrerror( & graphOut ) );
					exit(1);
				}
#ifdef VERBOSE
				printf( "AddEdge: from=%ld to=%ld cost=%ld user=%ld\n", nNodeFrom, nNodeTo, nCost, nUser);
#endif
			}
		}
	}
#ifndef VERBOSE
	printf( "\ndone.\n" );
#endif

	fclose( fp );

	regfree( & reVersion );
	regfree( & reByteOrder );
	regfree( & reNodeAttrSize );
	regfree( & reEdgeAttrSize );
	regfree( & reCounters );
	regfree( & reOpaque );
	regfree( & reNodeFrom );
	regfree( & reNodeAttr );
	regfree( & reEdge );
	regfree( & reToNodeAttr );
	regfree( & reEdgeAttr );

	if ( pbNodeAttr ) free( pbNodeAttr );
	if ( pbToNodeAttr ) free( pbToNodeAttr );
	if ( pbEdgeAttr ) free( pbEdgeAttr );


	printf( "Flatten..." ); fflush(stdout);
	nret = dglFlatten( & graphOut );
	if ( nret < 0 ) {
		fprintf( stderr, "dglFlatten error %s\n", dglStrerror( &graphOut ) );
		exit(1);
	}
	printf( "done.\n" );

	if ( pszGraphout ) {
		fd = open( pszGraphout , O_WRONLY | O_CREAT | O_TRUNC , 0666 );
		if ( fd < 0 ) {
			perror( "open" ); exit(1);
		}

		printf( "Write <%s>..." , pszGraphout ); fflush(stdout);
		nret = dglWrite( & graphOut , fd );
		if ( nret < 0 ) {
			fprintf( stderr, "dglWrite error %s\n", dglStrerror( &graphOut ) );
			exit(1);
		}
		printf( "done.\n" );
		close(fd);
	}

	printf( "Release..." ); fflush(stdout);
	dglRelease( &graphOut );
	printf( "done.\n" );

	return 0;
}
